package com.yunchao.library.socket.model.power

import com.yunchao.library.socket.constants.Values
import com.yunchao.library.socket.constants.Errors
import com.yunchao.library.socket.constants.Limits
import com.yunchao.library.socket.extensions.toReadOnlyBuffer
import java.nio.ByteBuffer
import java.nio.ByteOrder

sealed class PowerManagement {
    data class Payload(
        val action: Action,
        val flags: String
    ) {
        init {
            require(flags.matches(Regex("^[0-9a-fA-F]{8}$"))) {
                Errors.INVALID_FLAGS
            }
            when (action) {
                Action.POWER_ON -> require(flags.equals(Values.FLAG_EXCLUSIVE, true)) {
                    Errors.INVALID_FLAGS_POWER_ON
                }
                Action.POWER_OFF -> require(flags.equals(Values.FLAG_NORMAL, true)) {
                    Errors.INVALID_FLAGS_POWER_OFF
                }
            }
        }

        companion object {
            fun fromByteArray(bytes: ByteArray): Payload {
                val buffer = bytes.toReadOnlyBuffer()
                return Payload(
                    action = Action.fromValue(buffer.get().toUByte()),
                    flags = String.format("%08X", buffer.int)
                )
            }
        }
    }

    data class Result(
         val error: String = "",
        val atr: ByteArray = byteArrayOf()
    )  {
        init {
            if (error.isEmpty()) {
                if (atr.isNotEmpty()) require(atr.size in 1..Limits.MAX_KEY_SIZE) {
                    Errors.INVALID_ATR_LENGTH
                }
            } else {
                require(atr.isEmpty()) { "错误状态下ATR应答字符序列应为空" }
            }
        }

        companion object {
            fun powerOnSuccess(atr: ByteArray) = Result(atr = atr)
            fun powerOffSuccess() = Result()
            fun failure(error: String) = Result(error)
        }

         fun toByteArray(): ByteArray {
            val errorBytes = error.encodeToByteArray()
            return ByteBuffer.allocate(4 + errorBytes.size + 1 + atr.size).apply {
                order(ByteOrder.BIG_ENDIAN)
                putInt(errorBytes.size)
                put(errorBytes)
                put(atr.size.toByte())
                put(atr)
            }.array()
        }

        override fun equals(other: Any?): Boolean {
            if (this === other) return true
            if (javaClass != other?.javaClass) return false
            other as Result
            return error == other.error && atr.contentEquals(other.atr)
        }

        override fun hashCode(): Int {
            var result = error.hashCode()
            result = 31 * result + atr.contentHashCode()
            return result
        }
    }

    enum class Action(val value: UByte) {
        POWER_ON(0x01u), POWER_OFF(0x02u);

        companion object {
            fun fromValue(value: UByte): Action = Action.entries.firstOrNull { it.value == value }
                ?: throw IllegalArgumentException("${Errors.INVALID_ACTION}: 0x${value.toString(16)}")
        }
    }
}